/** \file
* WIN32-specific implementation of the Thread class.
* This file is part of Chorus, available at
* http://code.google.com/p/chorus-twitter/ . This file and all associated files
* and derivative works are bound by the terms of the LGPL 3.0 license,
* available at http://www.gnu.org/licenses/lgpl-3.0-standalone.html .
* \par Subversion URL:
* \$URL: http://chorus-twitter.googlecode.com/svn/branches/cpp/src/system/win32/Thread.cpp $
* \version \$Rev: 153 $
* \date    \$Date: 2010-04-06 07:43:42 +0000 (Tue, 06 Apr 2010) $
*/

#define NEEDS_WIN32
#include "config.h"

#include "system/DebugConsole.hpp"
#include "system/Thread.hpp"

using namespace std;

bool Thread::init()
{
	hthread = 0;
	threadid = 0;
	return true;
}

bool Thread::uninit()
{
	return true;
}

bool Thread::start()
{
	if (!isDone())
	{
		PDEBUG2("Thread already started:", (void*)threadid);
		return false;
	}

	hthread = CreateThread(0, 0, callback, this, 0, &threadid);
	if (!hthread)
	{
		PDEBUG_WIN32("CreateThread failed");
		return false;
	}
	return true;
}

bool Thread::pause()
{
	if (SuspendThread(hthread) == -1)
	{
		PDEBUG_WIN32("SuspendThread failed");
		return false;
	}
	return true;
}

bool Thread::resume()
{
	if (ResumeThread(hthread) == -1)
	{
		PDEBUG_WIN32("ResumeThread failed");
		return false;
	}
	return true;
}

bool Thread::abort()
{
	if (isDone())
		return true;
	PDEBUG2("Warning - thread aborted while still running:", (void*)threadid);

	if (!TerminateThread(hthread, 0))
	{
		PDEBUG_WIN32("TerminateThread failed");
		return false;
	}

	return freeHandle();
}

bool Thread::wait()
{
	return waitThenAbort(INFINITE);
}

bool Thread::waitThenAbort(unsigned timeoutms)
{
	if (isDone())
		return true;
	switch (WaitForSingleObject(hthread, timeoutms))
	{
	case WAIT_OBJECT_0:
		return freeHandle();
	case WAIT_TIMEOUT:
		PDEBUG2("Warning - Thread timed out:", (void*)threadid);
		return abort();
	default:
		PDEBUG_WIN32("WaitForSingleObject failed");
		return false;
	}
}

bool Thread::waitThenAbort(std::list<Thread*> &threads,	unsigned timeoutms)
{
	HANDLE *handles = new HANDLE[threads.size()];
	if (!handles)
	{
		PDEBUG("new failed");
		return false;
	}
	unsigned hcount = 0;

	for (list<Thread*>::const_iterator t = threads.begin();
		t != threads.end(); t++)
		if (!(*t)->isDone())
			handles[hcount++] = (*t)->hthread;
	if (!hcount)
	{
		delete [] handles;
		return true;
	}

	bool success = true;

	switch (WaitForMultipleObjects(hcount, handles, TRUE, timeoutms))
	{
	case WAIT_OBJECT_0:
		for (list<Thread*>::const_iterator t = threads.begin();
			t != threads.end(); t++)
			if ((*t)->hthread)
				success &= (*t)->freeHandle();
		break;

	case WAIT_TIMEOUT:
		for (list<Thread*>::const_iterator t = threads.begin();
			t != threads.end(); t++)
			success &= (*t)->abort();
		break;

	default:
		PDEBUG_WIN32("WaitForMultipleObjects failed");
		success = false;
	}

	delete [] handles;
	return success;
}

unsigned Thread::getCurrentThreadID()
{
	return GetCurrentThreadId();
}

bool Thread::isDone()
{
	if (!hthread)
		return true;
	switch (WaitForSingleObject(hthread, 0))
	{
	case WAIT_OBJECT_0:
		return freeHandle();
	case WAIT_TIMEOUT:
		// Time out immediately, so the thread is still busy. No problem.
		return false;
	default:
		PDEBUG_WIN32("WaitForSingleObject failed");
		return false;
	}
}

bool Thread::freeHandle()
{
	const bool success = CloseHandle(hthread);
	if (success)
		hthread = 0;
	else
		PDEBUG_WIN32("CloseHandle failed");
	return success;
}

unsigned long WINAPI Thread::callback(void *param)
{
	return (unsigned long)((Thread*)param)->callFunc();
}

bool Thread::callFunc()
{
	return call->call();
}
